package
{
	import SimpleAnimator.SimpleAnimator;
	import SimpleTileProvider.SimpleTileProvider;
	
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	
	import mx.core.FlexGlobals;
	
	import spark.components.Group;

	public class LRCommanderMgr
	{		
		public function LRCommanderMgr(enforcer:SingletonEnforcer) { ; }
		private static var m_mgr:LRCommanderMgr;
		
		public static function getInstance():LRCommanderMgr
		{
			if( m_mgr == null) {
				TraceManager.trace_msg(TraceManager.COMMANDER_MGR, TraceManager.TL_MEDIUM, "Creating new LRCommanderMgr instance." );
				m_mgr = new LRCommanderMgr(new SingletonEnforcer());
			}
			return m_mgr;
		}

		//
		// The LRCommanderMgr is a singleton manager class that manages the interactions between the
		// animators and tile providers.  It provides access to the list of tiles currently being animated
		// and the current animator.
		//

		private var m_activeTiles:Array = new Array(); 
		private var m_currentAnimator:ILRAnimator;

		public function get tileList():Array { return m_activeTiles; }
		public function get animationSurface():Group { return FlexGlobals.topLevelApplication.animationSurface; }
		public function get exposeScroller():ExposeScroller { return FlexGlobals.topLevelApplication.exposeScroller; }
		
		private var m_animators:Array = new Array();
		private var m_tileProviders:Array = new Array();
		
		public function initialize():void
		{
			TraceManager.trace_msg(TraceManager.COMMANDER_MGR, TraceManager.TL_MEDIUM, "initialize()" );
			
			// Create the arrays of known tile providers and animators.
			// Eventually these need to be loaded via modules.
			m_animators.push( new SimpleAnimator() as ILRAnimator);
			m_tileProviders.push( new SimpleTileProvider as ILRTileProvider );

			// Walk the array of animators and initialize them.
			for( var i:uint = 0; i < m_animators.length; i++ )
				m_animators[i].initialize(this);
			
			// Walk the array of tile providers and initialize them.
			for( var j:uint = 0; j < m_tileProviders.length; j++ )
				m_tileProviders[j].initialize(this);

			// Start the heartbeat timer.
			m_heartbeat.addEventListener(TimerEvent.TIMER, doHeartbeat );
			m_heartbeat.start();
		}

		// A heartbeat timer gives the manager an opportunity to run periodically and manage the interations
		// between the animators and the tile providers.  This is a very simple state machine and will probably
		// become much more sophisticated over time.
		
		private var m_heartbeat:Timer = new Timer( 1000 );
		private var m_animationHeartbeats:uint = 15;

		// The manager cycles through the following states: Preparing, Running.
		// n.b. This state machine is crap...  should probably be cleaned up.

		private var m_managerState:String = "Preparing";
		
		private var m_started:Boolean = false;
		private var m_paused:Boolean = false;
		private var m_expose:Boolean = false;
		
		private function doHeartbeat(e:TimerEvent):void
		{
			// If the current animator is paused, don't count down the animation cycle during heartbeats.
			if( m_currentAnimator && (m_currentAnimator.animationState == "Paused") ) 
				return;
			
			switch( m_managerState )
			{
				case "Preparing":
					doPrepareHeartbeat();
					break;
				
				case "Running":
					doRunHeartbeat();
					break;
			}
		}
		
		private function doPrepareHeartbeat():void
		{
			TraceManager.trace_msg(TraceManager.COMMANDER_MGR, TraceManager.TL_MEDIUM, "doPrepareHeartbeat" );

			// Clear the tile list and the animation surface.
			tileList.length = 0;
			animationSurface.removeAllElements();
			
			// Choose an animator at random.
			m_currentAnimator = m_animators[0];

			// Tell the tile providers to prepare.
			for( var j:uint = 0; j < m_tileProviders.length; j++ )
				m_tileProviders[j].prepareTiles(100);

			// Reset the heartbeat count and transition to running.
			m_animationHeartbeats = 180;
			m_started = false;
			m_managerState = "Running";
		}
		
		private function doRunHeartbeat():void
		{
			if( 0 == m_animationHeartbeats )
			{
				switch( m_currentAnimator.animationState )
				{
					case "Running":
					case "Paused":
						TraceManager.trace_msg(TraceManager.COMMANDER_MGR, TraceManager.TL_MEDIUM, "doRunHeartbeat - animation period has expired, stopping the animator" );
						m_currentAnimator.stopAnimation();
						break;

					case "Stopping":
						TraceManager.trace_msg(TraceManager.COMMANDER_MGR, TraceManager.TL_MEDIUM, "doRunHeartbeat - waiting for animator to stop" );
						break;
					
					case "Stopped":
						TraceManager.trace_msg(TraceManager.COMMANDER_MGR, TraceManager.TL_MEDIUM, "doRunHeartbeat - stopped" );
						m_managerState = "Preparing";
						break;						
				}
			}
			else
			{
				// See if all the tile providers are ready to run.
				var allPrepared:Boolean = true;
				for( var j:uint = 0; j < m_tileProviders.length; j++ )
				{
					if( false == m_tileProviders[j].prepared )
					{
						TraceManager.trace_msg(TraceManager.COMMANDER_MGR, TraceManager.TL_MEDIUM, "Still waiting for tile providers to be ready to run" );
						allPrepared = false;						
					}
				}

				if( allPrepared )
				{ 
					if( false == m_started )
					{
						TraceManager.trace_msg(TraceManager.COMMANDER_MGR, TraceManager.TL_MEDIUM, "Starting this animation." );
						m_currentAnimator.startAnimation();
						m_started = true;
					}
					else
					{
						m_animationHeartbeats--;
					}
				}
			}
		}
		
		public function togglePause():void
		{
			if( m_paused )
			{
				TraceManager.trace_msg(TraceManager.COMMANDER_MGR, TraceManager.TL_MEDIUM, "LRCommanderMgr::togglePause - resuming" );
				m_currentAnimator.startAnimation();
				m_paused = false;
			}
			else
			{
				TraceManager.trace_msg(TraceManager.COMMANDER_MGR, TraceManager.TL_MEDIUM, "LRCommanderMgr::togglePause - pausing" );
				m_currentAnimator.pauseAnimation();
				m_paused = true;
			}
		}

		public function toggleExpose():void
		{
			if( m_expose )
			{
				TraceManager.trace_msg(TraceManager.COMMANDER_MGR, TraceManager.TL_MEDIUM, "toggleExpose: leaving expose mode." );

				// TODO: Exit expose mode.

				togglePause();
				exposeScroller.visible = false;
				m_expose = false;
			}
			else
			{
				switch( m_managerState )
				{
					case "Preparing":
						TraceManager.trace_msg(TraceManager.COMMANDER_MGR, TraceManager.TL_MEDIUM, "toggleExpose: Ignoring expose request while preparing.  Try again while we're running." );
						break;
				
					case "Running":
						if( false == m_paused )
							togglePause();

						TraceManager.trace_msg(TraceManager.COMMANDER_MGR, TraceManager.TL_MEDIUM, "toggleExpose: entering expose mode." );
						
						// TODO: Enter expose mode.

						exposeScroller.visible = true
						m_expose = true;
						break;
				}
			}
		}
		
		public function restartAnimation(immediate:Boolean):void
		{
			if( m_managerState == "Running" )
			{
				m_animationHeartbeats = 0;
				if( immediate )
					m_currentAnimator.stopAnimation(true);
			}
		}

		public function doResize():void
		{
			restartAnimation(true);
		}
	}
	

}

class SingletonEnforcer { }

